#include "AffectsBip.h"
#include "time.h"

AffectsBip::AffectsBip(){
}

AffectsBip::AffectsBip(PKB* p){
	pkb = p;
	st = (*pkb).getStmtTable();
	varT = (*pkb).getVarTable();
	modifiesMap = (*pkb).getFullModifiesMap();
	usesMatrix = (*pkb).getFullUsesMatrix();
	totalLineNum = (*pkb).getTotalLineNum();
	affectsBipMatrix = vector<vector<int>>(totalLineNum+1, vector<int>(totalLineNum+1,0));
	CFGBip = (*pkb).getExtendProcedure();
	isBipComputed = isBipStarComputed = false;
}

void AffectsBip::ComputeAffectsBip(){
	for(int i=0;i<CFGBip.size();i++){
		int curIndex = 0;
		vModifiedByLine = vector<vector<int>>(varT.getSize(), vector<int>(1,-1));
		vector<int> pSeq = CFGBip.at(i);
		matrix_t mat;
		mat.m = vector<vector<int>>(pSeq.size(), vector<int>(pSeq.size(), 0));
		while(curIndex < pSeq.size()){
			int curLine = pSeq.at(curIndex);
			if(st.getLineDetails(curLine).type == "Assign")
				curIndex = DealWithAssign(curIndex, pSeq, &mat);
			else if(st.getLineDetails(curLine).type == "While")
				curIndex = DealWithWhile(curIndex, pSeq, &mat);
			else if(st.getLineDetails(curLine).type == "If")
				curIndex = DealWithIf(curIndex, pSeq, &mat);
			else if(st.getLineDetails(curLine).type == "Call")
				curIndex += 1;
		}
		mtx.push_back(mat);
	}
	isBipComputed = true;
}

int AffectsBip::DealWithAssign(int curIndex, vector<int> seq, matrix_t* mat){
	int curLine = seq.at(curIndex);
	for(int i=0;i<usesMatrix.at(curLine).size();i++){
		if(usesMatrix.at(curLine).at(i) == 1){
			if(vModifiedByLine.at(i).at(0) != -1)
				for(int j=0;j<vModifiedByLine.at(i).size();j++){
					(*mat).m.at(vModifiedByLine.at(i).at(j)).at(curIndex) = 1;
					int f = seq.at(vModifiedByLine.at(i).at(j)), s = seq.at(curIndex);
					if(affectsBipMatrix.at(f).at(s) == 0)
						affectsBipList.push_back(pair<int,int>(f,s));
					affectsBipMatrix.at(f).at(s) = 1;
				}
		}
	}

	// Update tracking tables
	int varModified = modifiesMap.at(curLine);
	vModifiedByLine.at(varModified).clear();
	vModifiedByLine.at(varModified).push_back(curIndex);

	return curIndex+1;
}

while_t AffectsBip::findWhileIndexMap(int index, vector<int> seq){
	int line = seq.at(index);
	while_t lineW  = st.getWhileDetailFromLine(line), w;

	w.lineNum = index;
	w.loopStartLine = index+1;
	for(int i=index+1;i<seq.size();i++)
		if(lineW.loopEndLine == seq.at(i)){
			w.loopEndLine = i;
			break;
		}

	return w;
}

int AffectsBip::DealWithWhile(int curIndex, vector<int> seq, matrix_t* mat){
	vector<vector<int>> backUpVMList(vModifiedByLine);
	
	while_t w = findWhileIndexMap(curIndex, seq);

	int i = w.loopStartLine;
	while(i<=w.loopEndLine){
		if(st.getLineDetails(seq.at(i)).type == "Assign")
			i = DealWithAssign(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "If")
			i = DealWithIf(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "While")
			i = DealWithWhile(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "Call")
			i = i+1;
	}

	// Do it second time to deal with the new track list
	i = w.loopStartLine;
	while(i<=w.loopEndLine){
		if(st.getLineDetails(seq.at(i)).type == "Assign")
			i = DealWithAssign(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "If")
			i = DealWithIf(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "While")
			i = DealWithWhile(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "Call")
			i = i+1;
	}

	vModifiedByLine = mergeVMList(backUpVMList, vModifiedByLine);
	return w.loopEndLine+1;
}

if_t AffectsBip::findIfIndexMap(int index, vector<int> seq){
	int line = seq.at(index);
	if_t l = st.getIfDetailFromLine(line), r;
	
	r.lineNum = index;
	r.thenStartLine = index+1;
	for(int i=r.thenStartLine;i<seq.size();i++){
		if(seq.at(i) == l.thenEndLine)
			r.thenEndLine = i;
		if(seq.at(i) == l.elseStartLine)
			r.elseStartLine = i;
		if(seq.at(i) == l.elseEndLine){
			r.elseEndLine = i;
			break;
		}
	}
	return r;
}
	
int AffectsBip::DealWithIf(int curIndex, vector<int> seq, matrix_t* mat){
	if_t l = findIfIndexMap(curIndex, seq);

	// Back up the tracking list.
	vector<vector<int>> backUpVModList = vector<vector<int>>(vModifiedByLine);
	int i=l.thenStartLine;
	while(i<=l.thenEndLine){
		if(st.getLineDetails(seq.at(i)).type == "Assign")
			i = DealWithAssign(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "While")
			i = DealWithWhile(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "If")
			i = DealWithIf(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "Call")
			i = i+1;
	}
	vector<vector<int>> thenVModList = vector<vector<int>>(vModifiedByLine);
	// Get back the tracking list
	vModifiedByLine = vector<vector<int>>(backUpVModList);

	i=l.elseStartLine;
	while(i<=l.elseEndLine){
		if(st.getLineDetails(seq.at(i)).type == "Assign")
			i = DealWithAssign(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "While")
			i = DealWithWhile(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "If")
			i = DealWithIf(i, seq, mat);
		else if(st.getLineDetails(seq.at(i)).type == "Call")
			i = i+1;
	}
	vector<vector<int>> elseVModList = vector<vector<int>>(vModifiedByLine);

	vModifiedByLine = mergeVMList(thenVModList, elseVModList);
	return l.elseEndLine+1;
}

vector<vector<int>> AffectsBip::mergeVMList(vector<vector<int>> list1, vector<vector<int>> list2){
	vector<vector<int>> result;
	for(int i=0;i<varT.getSize();i++){
		vector<int> l1(list1.at(i)), l2(list2.at(i)), r(l1.size()+l2.size());
		vector<int>::iterator it = set_union(l1.begin(),l1.end(), l2.begin(),l2.end(), r.begin());
		r.resize(it-r.begin());
		if(r.size() > 1 && r.at(0) == -1)
			r.erase(r.begin());
		result.push_back(r);
	}
	return result;
}

void AffectsBip::ComputeAffectsBipStar(){
	affectsBipStarMatrix = vector<vector<int>>(affectsBipMatrix);
	affectsBipStarList = vector<pair<int,int>>(affectsBipList);
	for(int s=0;s<mtx.size();s++){
		vector<int> seq = CFGBip.at(s);
		vector<vector<int>> matrix(mtx.at(s).m);
		for(int i=0;i<matrix.size();i++){
			for(int j=0;j<matrix.size();j++)
				if(matrix.at(i).at(j) == 0){
					for(int k=0;k<matrix.size();k++)
						if(matrix.at(i).at(k) == 1 && matrix.at(k).at(j) == 1){
							matrix.at(i).at(j) = 1;
							int row = seq.at(i), col = seq.at(j);
							if(affectsBipStarMatrix.at(row).at(col) == 0)
								affectsBipStarList.push_back(pair<int,int>(row,col));
							affectsBipStarMatrix.at(row).at(col) = 1;
						}
				}
		}

		for(int i=0;i<matrix.size();i++){
			if(matrix.at(i).at(i) == 0)
				for(int k=0;k<matrix.size();k++)
					if(matrix.at(i).at(k) == 1 && matrix.at(k).at(i) == 1){
						matrix.at(i).at(i) = 1;
						int row = seq.at(i),col = seq.at(i);
						if(affectsBipStarMatrix.at(row).at(col) == 0)
							affectsBipStarList.push_back(pair<int,int>(row,col));
						affectsBipStarMatrix.at(row).at(col) = 1;
					}
		}
	}
	isBipStarComputed = true;
}

vector<pair<int,int>> AffectsBip::GetAffectsBipList(){
	return affectsBipList;
}

vector<vector<int>> AffectsBip::GetAffectsBipMatrix(){
	return affectsBipMatrix;
}

vector<pair<int,int>> AffectsBip::GetAffectsBipStarList(){
	return affectsBipStarList;
}

vector<vector<int>> AffectsBip::GetAffectsBipStarMatrix(){
	return affectsBipStarMatrix;
}

bool AffectsBip::IsAffectsBipComputed(){
	return isBipComputed;
}

bool AffectsBip::IsAffectsBipStarComputed(){
	return isBipStarComputed;
}